using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Xml.Serialization;

namespace zEngine.Cameras
{
    public class Camera
    {
        #region Fields
        public float AspectRatio;
        public float NearPlane = 10.0f;
        public float FarPlane = 10000.0f;
        public float ViewAngle = MathHelper.PiOver4;

        [XmlIgnore]
        public Game Game;
        [XmlIgnore]
        public GraphicsDevice GraphicDevice = null;

        public Vector3 CameraPosition = Vector3.Zero;
        public Vector3 CameraTarget = Vector3.Zero;
        public Vector3 CameraUp = new Vector3(0, 1, 0);

        [XmlIgnore]
        public Matrix ProjectionMatrix;
        [XmlIgnore]
        public Matrix ViewMatrix;
        public Vector3 FollowObject = Vector3.Zero;
        [XmlIgnore]
        public Vector3 ViewVector;
        public Vector3 RangeFromObject = new Vector3(0, 200, 300);
        #endregion

        /// <summary>
        /// Initializes a new instance of the Camera class.
        /// </summary>
        public Camera()
        {
        }

        public Camera(Game game, Vector3 cameraPosition, Vector3 cameraTarget)
        {
            Game = game;
            CameraPosition = cameraPosition;
            CameraTarget = cameraTarget;
        }

        /// <summary>
        /// Recalculate the matrix we need
        /// </summary>
        public virtual void Update(GameTime gameTime)
        {
            // Get graphic device if need
            if (GraphicDevice == null)
            {
                GraphicDevice = Game.GraphicsDevice;
            }

            ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(ViewAngle, GraphicDevice.Viewport.AspectRatio, NearPlane, FarPlane);
            ViewMatrix = Matrix.CreateLookAt(CameraPosition, CameraTarget, CameraUp);
            ViewVector = Vector3.Transform(CameraTarget - CameraPosition, Matrix.CreateRotationY(0));
            ViewVector.Normalize();
        }

        #region Dynamic Properties
        public Vector3 Direction
        {
            get
            {
                Vector3 CurrentDirection = CameraTarget - CameraPosition;
                CurrentDirection.Normalize();
                return CurrentDirection;
            }
        }
        public BoundingFrustum Frustum
        {
            get
            {
                return new BoundingFrustum(ViewMatrix * ProjectionMatrix);
            }
        }
        #endregion
        /// <summary>
        /// Set the current camera as the main camera using in the game
        /// </summary>
        public void SetToMainCamera()
        {
            Game.Services.RemoveService(typeof(Camera));
            Game.Services.AddService(typeof(Camera), this);
        }
        /// <summary>
        /// Get the current main cameraUp in the input game object
        /// </summary>
        public static Camera GetMainCamera(Game game)
        {
            return (Camera)game.Services.GetService(typeof(Camera));
        }

    }
}
